﻿
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO.Ports;
using System.IO;
using System.Diagnostics;
using n_MySerialPort;
using n_VexProtocol;
using n_CopyRightForm;

namespace n_VexLoaderForm
{
	public partial class VexLoaderForm : Form
	{
		public delegate void deleProgressStep( int Max, int Current );
		public static deleProgressStep ProgressStep;
		
		Process Proc;
		delegate void DelReadStdOutput(string result);
		event DelReadStdOutput ReadStdOutput;
		
		SerialPort port;
		byte[] ReadBuffer;
		
		byte[] Buffer;
		
		//发送字节码
		byte[] BufferHead;
		
		string BFile;
		
		bool NeedShow;
		
		int ClickNumber = 0;
		
		public static int RAM_Size;
		public static int ROM_Size;
		public static int UseROM;
		public static int UseRAM;
		
		string CPU_VersionList;
		bool VersionUpShow;
		
		n_CopyRightForm.CopyRightForm VBox;
		
		
		//构造函数
		public VexLoaderForm()
		{
			InitializeComponent();
			
			ReadBuffer = new byte[ 100 ];
			
			Buffer = new byte[100000];
			BufferHead = new byte[6];
			
			this.Visible = false;
			
			ReadStdOutput += new DelReadStdOutput(ReadStdOutputAction);
			
			VexProtocol.Init();
			VexProtocol.ReceiveType = ReType;
			NeedShow = true;
			
			AddVersion( "ESP32:          2.030.A" );
			AddVersion( "GD32E103CBT6:   2.030.B" );
			AddVersion( "GD32E230C8T6:   2.031.A" );
			AddVersion( "GD32E231C8T6:   2.031.A" );
			AddVersion( "GD32F303CGT6:   2.030.A" );
			AddVersion( "GD32F303RGT6:   2.030.A" );
			AddVersion( "GD32VF103C8T6:  2.030.B" );
			AddVersion( "GD32VF103CBT6:  2.030.B" );
			AddVersion( "STM32F103C8T6:  2.030.A" );
			AddVersion( "STM32F103RCT6:  2.030.A" );
			AddVersion( "STM32F103RET6:  2.030.A" );
			AddVersion( "STM32F103ZET6:  2.030.A" );
			AddVersion( "STM32F405RGT6:  2.030.A" );
			AddVersion( "STM32F411RET6:  2.030.A" );
			
			CPU_VersionList = CPU_VersionList.TrimEnd( '\n' );
		}
		
		void AddVersion( string v )
		{
			CPU_VersionList += v + "\n";
		}
		
		//运行窗体
		public void Run( string vFile )
		{
			if( n_AVRdude.AVRdude.BoardType == "Curie" ) {
				this.comboBox波特率.Text = "9600";
			}
			else {
				this.comboBox波特率.Text = "115200";
			}
			
			BFile = vFile;
			
			labelVersion.Text = "";
			labelMes.Text = "";
			
			Width = 550;
			Height = 371;
			
			if( checkBoxHide.Checked ) {
				ButtonOkClick( null, null );
			}
			if( NeedShow ) {
				
				VersionUpShow = false;
				
				//注意,直接设置Visible不能使窗体获得焦点
				//this.Visible = true;
				this.ShowDialog();
			}
			
			//while( this.Visible ) {
			//	System.Windows.Forms.Application.DoEvents();
			//}
			//return;
		}
		
		//============================================================
		
		//确定键按下
		void ButtonOkClick(object sender, EventArgs e)
		{
			buttonOk.Enabled = false;
			timer1.Enabled = true;
			
			richTextBox1.Text = "";
			richTextBox2.Text = "";
			labelVersion.Text = "";
			
			if( port == null || !port.IsOpen ) {
				if( TryOpenPortError() ) {
					timer1.Enabled = false;
					NeedShow = true;
					labelMes.Text = "请选择一个有效的串口号";
					buttonOk.Enabled = true;
					return;
				}
			}
			
			VexProtocol.Result = "";
			
			//发送程序数据
			string err = SendProgramOK();
			
			if( err == "ROM" || err == "RAM" ) {
				if( err == "ROM" ) {
					MessageBox.Show( "程序量已超出主板ROM容量, 请重新点击下载按钮, 系统将自动进行优化..." );
				}
				else {
					MessageBox.Show( "数据区已超出主板RAM容量, 请重新点击下载按钮, 系统将自动进行优化..." );
				}
				buttonOk.Enabled = true;
				timer1.Enabled = false;
				
				this.Visible = false;
				if( port != null && port.IsOpen ) {
					port.Close();
				}
				port = null;
				return;
			}
			
			if( err != null ) {
				labelMes.Text = err;
			}
			
			//关闭串口
			if( !this.checkBoxHoldingPort.Checked ) {
				System.Threading.Thread.Sleep( 100 );
				try {
					port.DtrEnable = false;
					port.RtsEnable = false;
					port.Close();
				} catch {
					MessageBox.Show( "串口关闭失败" );
				}
				port = null;
			}
			timer1.Enabled = false;
			buttonOk.Enabled = true;
			
			NeedShow = (err != null) || checkBoxHoldingPort.Checked;
			
			if( sender != null ) {
				this.Visible = NeedShow;
			}
		}
		
		bool TryOpenPortError()
		{
			if( this.comboBoxPort.SelectedIndex == -1 ) {
				//MessageBox.Show( "请选择端口!" );
				if( checkBoxHide.Checked ) {
					//this.Visible = true;
				}
				return true;
			}
			try {
				string Name = this.comboBoxPort.Text;
				if( Name.StartsWith( "COM1 " ) ) {
					if( MessageBox.Show( "您可能选择了错误的的串口号， 一般来说不能使用COM1下载程序，需要选择硬件主板所对应的串口号。您确定要继续吗？",  "注意", MessageBoxButtons.YesNo ) == DialogResult.No ) {
						return true;
					}
				}
				string pname = MySerialPort.GetComName( Name );
				port = new System.IO.Ports.SerialPort( pname );
				port.BaudRate = int.Parse( this.comboBox波特率.Text );
				port.DataBits = 8;
				port.Parity = System.IO.Ports.Parity.None;
				port.StopBits = System.IO.Ports.StopBits.One;
				port.ReadTimeout = 1;
				if( checkBoxDTR_RTS.Checked ) {
					port.DtrEnable = true;
					port.RtsEnable = true;
				}
				port.DataReceived += new SerialDataReceivedEventHandler( DataReceived );
				port.Open();
				
				//不加这个可能会读取版本失败
				System.Threading.Thread.Sleep( 200 );
				
				n_AVRdude.AVRdude.PortName = pname;
			}
			catch {
				MessageBox.Show( "串口打开失败, 请先关闭其他占用串口的程序" );
				return true;
			}
			return false;
		}
		
		string SendProgramOK()
		{
			n_AVRdude.AVRdude.OK = false;
			
			try {
			//----------------------------------------
			//导入文件
			int RNumber = 0;
			if( BFile == null ) {
				RNumber = n_Decode.Decode.BCLength;
				for( int i = 0; i < n_Decode.Decode.BCLength; ++i ) {
					Buffer[i] = n_Decode.Decode.ByteCodeList[i];
				}
			}
			else {
				FileStream fs = File.Open( BFile, FileMode.OpenOrCreate );
				RNumber = fs.Read( Buffer, 0, Buffer.Length );
				fs.Close();
			}
			
			//计算校验和
			byte CheckSum = 0;
			for( int i = 0; i < RNumber; ++i ) {
				CheckSum ^= Buffer[i];
			}
			
			richTextBox1.Text += "CHECK:" + CheckSum.ToString( "X" ).PadLeft( 2, '0' ) + " NUM:" + RNumber + " -> ";
			
			//----------------------------------------
			//读取版本信息
			VexProtocol.ClearReType();
			BufferHead[0] = 0xAA;
			BufferHead[1] = 0x01;
			BufferHead[2] = 0x00;
			BufferHead[3] = 0xFF;
			port.Write( BufferHead, 0, 4 );
			if( WaitReceiveError( VexProtocol.VM_VERSION, 5 ) ) {
				
				//MessageBox.Show( "vos引擎版本获取失败. 可尝试按下复位键后, 再立刻点击下载按钮, 1秒钟之内有效", "请尝试按下复位键后再下载" );
				return "vos引擎版本获取失败. 可尝试按一下复位键, 再立刻点击下载按钮, 1秒钟之内有效";
			}
			//版本校验
			
			int bV1 = VexProtocol.RE_Data[4];
			int bV2 = VexProtocol.RE_Data[5];
			char bVX = '0';
			string VosMes = null;
			if( bV1 > 2 || bV1 == 2 && bV2 > 2 ) {
				bVX = (char)VexProtocol.RE_Data[8];
				int i = 9;
				while( VexProtocol.RE_Data[i] != 0 ) {
					VosMes += ((char)VexProtocol.RE_Data[i]).ToString();
					i++;
					if( i > 200 ) {
						break;
					}
				}
			}
			
			RAM_Size = 1024 * VexProtocol.RE_Data[6];
			ROM_Size = 1024 * VexProtocol.RE_Data[7];
			labelVersion.Text = "v" + VexProtocol.RE_Data[4] + "." + VexProtocol.RE_Data[5].ToString().PadLeft( 3, '0' ) + "." + (char)bVX +
				" (RAM:" + VexProtocol.RE_Data[6] + "K ROM:" + VexProtocol.RE_Data[7] + "K)";
			
			//这里临时增加ROM溢出判断
			if( UseROM >= ROM_Size ) {
				return "ROM";
			}
			if( UseRAM >= RAM_Size ) {
				return "RAM";
			}
			//版本匹配
			int cV1 = 0;
			int cV2 = 0;
			char cVX = (char)0;
			string CPU = null;
			if( VosMes != null ) {
				string[] line0 = CPU_VersionList.Split( '\n' );
				string[][] CPUVL = new string[line0.Length][];
				for( int i = 0; i < line0.Length; ++i ) {
					CPUVL[i] = line0[i].Split( ':' );
					CPUVL[i][1] = CPUVL[i][1].Trim( " \t".ToCharArray() );
				}
				string[] line = VosMes.Split( ',' );
				for( int i = 0; i < line.Length; ++i ) {
					string[] cut = line[i].Split( ':' );
					if( cut[0] == "CPU" ) {
						CPU = cut[1];
						break;
					}
				}
				if( CPU != null ) {
					bool ok = false;
					for( int j = 0; j < CPUVL.Length; ++j ) {
						if( CPU == CPUVL[j][0] ) {
							string[] vl = CPUVL[j][1].Split( '.' );
							cV1 = int.Parse( vl[0] );
							cV2 = int.Parse( vl[1] );
							cVX = vl[2][0];
							ok = true;
							break;
						}
					}
					if( !ok ) {
						//... 软件版本比较旧 ... 可以忽略这个情况
					}
				}
			}
			if( cV1 == 0 || cV2 == 0 ) {
				cV1 = 2;
				cV2 = 30;
				cVX = 'A';
			}
			if( bV1 < cV1 || (bV1 == cV1 && bV2 < cV2) || (bV1 == cV1 && bV2 == cV2 && bVX < cVX) ) {
				if( CPU == null ) {
					CPU = "未知型号";
				}
				
				/*
				MessageBox.Show( "检测到主板的vos引擎版本为 " + bV1 + "." + bV2 + bVX + "[" + CPU + "], 已过时.\n" +
				                 "强烈建议升级到针对[" + CPU + "]的最新版本 " + cV1 + "." + cV2 + cVX + "\n" +
				                 "不升级的话部分模块可能运行异常.\n各型号CPU的VOS最新版本列表:\n" + CPU_VersionList,
				                 "vos版本更新提示" );
				*/
				
				if( !VersionUpShow ) {
					
					string mes = "主板的vos引擎版本为" + bV1 + "." + bV2.ToString().PadLeft( 3, '0' ) + "." + bVX + "[" + CPU + "], 已过时. " +
				                 "强烈建议升级到针对[" + CPU + "]的最新版本" + cV1 + "." + cV2.ToString().PadLeft( 3, '0' ) + "." + cVX + ".\n" +
				                 "不升级的话程序可能运行异常. ";
					
					VersionUpShow = true;
					labelVersionList.Text = "各CPU的VOS最新版本列表:\n" + CPU_VersionList;
					
					Width = 768;
					Height = 398;
					return mes + "再点击一下<开始下载>按钮将继续下载程序到主板.";
				}
			}
			
			//----------------------------------------
			//设置地址
			VexProtocol.ClearReType();
			BufferHead[0] = 0xAA;
			BufferHead[1] = 0x03;
			BufferHead[2] = 0x00;
			BufferHead[3] = 0xF0;
			BufferHead[4] = 0x00;
			BufferHead[5] = 0x00;
			port.Write( BufferHead, 0, 6 );
			//设置地址时会清空flash, 时间较长. 时间单位: 0.1秒
			if( WaitReceiveError( VexProtocol.VM_SETADDR, 50 ) ) {
				NeedShow = true;
				return "设置下载起始地址无回应";
			}
			//----------------------------------------
			//循环写入数据
			int Times = RNumber / 256;
			int Mode = RNumber % 256;
			for( int i = 0; i < Times; ++i ) {
				
				VexProtocol.ClearReType();
				BufferHead[0] = 0xAA;
				BufferHead[1] = 0x01;
				BufferHead[2] = 0x01;
				BufferHead[3] = 0xF1;
				port.Write( BufferHead, 0, 4 );
				port.Write( Buffer, i * 256, 256 );
				if( WaitReceiveError( VexProtocol.VM_STORE, 10 ) ) {
					NeedShow = true;
					return "写入数据无回应";
				}
				if( ProgressStep != null ) {
					ProgressStep( Times, i );
				}
			}
			//----------------------------------------
			//写入剩余数据
			if( Mode != 0 ) {
				
				VexProtocol.ClearReType();
				BufferHead[0] = 0xAA;
				BufferHead[1] = (byte)(Mode + 1);
				BufferHead[2] = 0;
				BufferHead[3] = 0xF1;
				port.Write( BufferHead, 0, 4 );
				port.Write( Buffer, Times * 256, Mode );
				
				if( WaitReceiveError( VexProtocol.VM_STORE, 10 ) ) {
					NeedShow = true;
					return "写入结束数据无回应";
				}
			}
			//----------------------------------------
			//读取校验和
			VexProtocol.ClearReType();
			BufferHead[0] = 0xAA;
			BufferHead[1] = 0x01;
			BufferHead[2] = 0x00;
			BufferHead[3] = 0xF3;
			port.Write( BufferHead, 0, 4 );
			if( WaitReceiveError( VexProtocol.VM_CHECK, 10 ) ) {
				NeedShow = true;
				return "读取校验和无回应";
			}
			}
			catch(Exception e) {
				return "下载过程发生错误!\n" + e;
			}
			n_AVRdude.AVRdude.OK = true;
			
			//数据校验
			return null;
		}
		
		bool WaitReceiveError( byte Tar, int time )
		{
			Tick = 0;
			while( VexProtocol.RE_Type != Tar ) {
				if( Tick > time ) {
					richTextBox1.Text += "没有回应数据, 结束下载.";
					return true;
				}
				Application.DoEvents();
			}
			return false;
		}
		
		void ReType( byte retype )
		{
			this.richTextBox1.Text += retype.ToString( "X" ).PadLeft( 2, '0' ) + " ";
		}
		
		void DataReceived(object sender, EventArgs e)
		{
			try {
				int n = port.Read(ReadBuffer, 0, 100 );
				if( n == 0 ) {
					return;
				}
				for( int i = 0; i < n; ++i ) {
					byte b = ReadBuffer[ i ];
					
					//this.richTextBox1.Text += b.ToString( "X" ).PadLeft( 2, '0' ) + " ";
					//this.richTextBox2.Text += ((char)b).ToString();
					
					VexProtocol.Deal( b );
				}
				richTextBox2.Text = VexProtocol.Result;
			}
			catch {
				
			}
		}
		
		//============================================================
		
		//窗口关闭
		void AVRdudeSetFormFormClosing(object sender, FormClosingEventArgs e)
		{
			this.Visible = false;
			if( port != null && port.IsOpen ) {
				port.Close();
			}
			port = null;
			
			e.Cancel = true;
		}
		
		void ComboBoxPortClick(object sender, EventArgs e)
		{
			//需要调用两次
			//RefreshPortList();
			//RefreshPortList();
			
			this.comboBoxPort.Items.Clear();
			
			//通过WMI获取COM端口
			string[] ss = MySerialPort.GetFullNameList();
			if( ss != null ) {
				this.comboBoxPort.Items.AddRange( ss );
			}
		}
		
		void RefreshPortList()
		{
			this.comboBoxPort.Items.Clear();
			
			//通过WMI获取COM端口
			//string[] ss = PortInfo.GetHardwareInfo(PortInfo.HardwareEnum.Win32_SerialPort, "Name" );
			
			//获取端口列表
			Microsoft.VisualBasic.Devices.Computer pc = new Microsoft.VisualBasic.Devices.Computer();
			foreach( string name in pc.Ports.SerialPortNames ) {
				
				string t = name;
//				foreach (string s in ss ) {
//					if( s.Contains( name ) ) {
//						t += s;
//					}
//				}
				this.comboBoxPort.Items.Add( t );
			}
			if( this.comboBoxPort.Items.Count != 0 ) {
				this.comboBoxPort.SelectedIndex = 0;
			}
		}
		
		void ButtonInstClick(object sender, EventArgs e)
		{
			//先显示出来让用户关闭, 因为目前还不能自动关闭
			//Proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
			
			//Proc.StartInfo.UseShellExecute = false;
			//Proc.StartInfo.CreateNoWindow = true;
			//Proc.StartInfo.RedirectStandardOutput = true;
			
			//Proc.StartInfo.RedirectStandardOutput = true; // 重定向标准输出
			//Proc.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
			
			
			if( Proc == null ) {
				Proc = new Process();
			}
			
			Proc.StartInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory + @"Resource\tools\ST-LINK Utility";
			
			Proc.StartInfo.FileName = @"ST-LINK_CLI.exe";//调用汇编命令
			
			string HexFile = @"Resource\remo-os\nucleo-f411re\Nucleo411.hex";
			
			Proc.StartInfo.Arguments = "-P \"" + (AppDomain.CurrentDomain.BaseDirectory + HexFile) + "\" -Rst -Run";
			
			Proc.Start();
		}
		
		void p_OutputDataReceived(object sender, DataReceivedEventArgs e)  
		{
			if (e.Data != null) {
				// 4. 异步调用，需要invoke
				this.Invoke(ReadStdOutput, new object[] { e.Data });
			}
		}
		
		void ReadStdOutputAction(string result)
		{
			//this.textBoxShowStdRet.AppendText(result + "\n");
		}
		
		int Tick;
		void Timer1Tick(object sender, EventArgs e)
		{
			Tick++;
		}
		
		void ButtonOkMouseDown(object sender, MouseEventArgs e)
		{
			if( e.Button == MouseButtons.Right ) {
				ClickNumber++;
				if( ClickNumber > 10 ) {
					this.FormBorderStyle = FormBorderStyle.Sizable;
					checkBoxHoldingPort.Checked = true;
				}
			}
		}
		
		void ButtonOpenVEXClick(object sender, EventArgs e)
		{
			string fileToSelect = n_OS.OS.SystemRoot + "vos" + n_OS.OS.PATH_S + "vos简介.doc";
			
			string args = string.Format("/Select, {0}", fileToSelect);
			System.Diagnostics.Process.Start( new System.Diagnostics.ProcessStartInfo("Explorer.exe", args ) );
		}
		
		void ButtonReadClick(object sender, EventArgs e)
		{
			VexProtocol.ClearReType();
			BufferHead[0] = 0xAA;
			BufferHead[1] = 0x03;
			BufferHead[2] = 0x00;
			BufferHead[3] = 0xF2;
			BufferHead[4] = 0;
			BufferHead[5] = 0;
			port.Write( BufferHead, 0, 6 );
		}
		
		void ButtonDriverClick(object sender, EventArgs e)
		{
			string fileToSelect = n_OS.OS.SystemRoot + "常见USB接口芯片驱动" + n_OS.OS.PATH_S + "新版CH341usb_com驱动.EXE";
			
			string args = string.Format("/Select, {0}", fileToSelect);
			System.Diagnostics.Process.Start( new System.Diagnostics.ProcessStartInfo("Explorer.exe", args ) );
		}
		
		void ButtonVersionClick(object sender, EventArgs e)
		{
			if( VBox == null ) {
				VBox = new n_CopyRightForm.CopyRightForm();
			}
			VBox.Run();
		}
	}
}

